/**
 * 20220112:废弃
 */
let THREE=require('three');
import Props from '../general/props';
import Utils from '../general/utils';

/**
 * 拖动配置序列所支持类型的对象
 * @param values {object}
 * enabled:true, 是否可用
 * closestRange:0.01, 最近筛选的范围（距离相差在这一范围内即视为与参考对象处于同一距离）
 * onlyClosest:false, 仅筛选距离最近的目标(按渲染顺序排序)
 * shiftFrontBack:true, 切换前后（拖动）
 * stopWhenPointerOut:true, pointOut事件时停止操作
 *
 * 事件：
 *   dragStart,
 *   dragIn,
 *   dragEnd
 *   示例：
 *      let callback=(e)=>{};
 *      drag.eventCenter.addEventListener('dragStart',callback);
 *
 * 添加配置后才有效，如添加了dragMeshConfig后就可以添加并拖动网格对象
 * 拖动配置需要指定约束类型，比如，一般的dragFreeLimit...
 */
export default class Drag{
    constructor(camera,rect,crossEvent,values={}){
        console.warn('this class is deprecated now ,use the new Drag witch support XR!');
        this.type='Drag';
        this.crossEvent=crossEvent;
        this._eventCenter=new THREE.EventDispatcher();
        this.camera=camera;
        this._rect=Utils.copy(rect);
        this._values=values;
        this._init();
    }

    //-------------------------------对外属性及方法------------------------------------

    /**
     * 事件对象
     * @returns {EventDispatcher}
     */
    get eventCenter(){
        return this._eventCenter;
    }

    /**
     * 设置rect（窗体resize时需更新rect）
     * @param rect
     */
    set rect(rect){
        if(Array.isArray(rect)){
            this._rect.left=rect[0];
            this._rect.top=rect[1];
            this._rect.width=rect[2];
            this._rect.height=rect[3];
        }else{
            this._rect.left=rect.left;
            this._rect.top=rect.top;
            this._rect.width=rect.width;
            this._rect.height=rect.height;
        }
    }

    /**
     * 设置拖动对象的类型优先顺序（拾取距离相同时影响）
     * @param classes
     */
    set order(classes){
        this._orders=classes;
    }

    /**
     * 获取类型优先顺序
     * @returns {*|null|[]}
     */
    get order(){
        return this._orders;
    }

    /**
     * 获取可拖动对象
     * @returns {[]|null}
     */
    get targets(){
        return this._objects;
    }


    /**
     * 释放未完成的动作
     */
    releaseUnfinishedEvent(){
        this._onPointerUp();
    }

    /**
     * 添加拖动配置
     * @param config
     */
    addConfig(config){
        let idx=-1;
        for(let i=0;i<this._config.length;i++){
            if(this._config[i].type===config.type){
                idx=i;
                break;
            }
        }
        if(idx>-1)return;
        this._config.push(config);
    }

    /**
     * 移除拖动配置
     * @param config
     */
    removeConfig(config){
        let idx=-1;
        for(let i=0;i<this._config.length;i++){
            if(this._config[i].type===config.type){
                idx=i;
                break;
            }
        }
        if(idx<0)return;
        this._config.splice(idx,1);
    }

    /**
     * 清除拖动配置
     */
    clearConfig(){
        this._config=[];
    }

    /**
     * 添加可拖动对象
     * @param objects
     */
    add(...objects) {
        for(let object of objects) {
            let config=this._getConfig(object);
            if(!config)continue;
            let idx=this._getObjectIndex(object,config);
            if(idx>-1)continue;
            this._objects.push(object);
        }
    }

    /**
     * 移除可拖动对象
     * @param objects
     */
    remove(...objects) {
        for(let object of objects) {
            let config=this._getConfig(object);
            if(!config)continue;
            let idx=this._getObjectIndex(object,config);
            if(idx<0)continue;
            this._objects.splice(idx,1);
        }
    }

    /**
     * 清除可拖动对象
     */
    clear() {
        this._objects = [];
    }

    /**
     * 添加中断对象
     * @param breakers
     */
    addBreaker(...breakers){
        for(let br of breakers){
            let has=false;
            for(let b of this._breaker){
                if(b.uuid===br.uuid){
                    has=true;
                    break;
                }
            }
            if(!has)this._breaker.push(br);
        }
    }

    /**
     * 移除中断对象
     * @param breakers
     */
    removeBreaker(...breakers){
        for(let br of breakers){
            for(let i=0;i<this._breaker.length;i++){
                if(this._breaker[i].uuid===br.uuid){
                    this._breaker.splice(i,1);
                    break;
                }
            }
        }
    }

    /**
     * 清除中断对象
     */
    clearBreaker(){
        this._breaker=[];
    }

    /**
     * 销毁
     */
    dispose() {
        this.releaseUnfinishedEvent();
        this.crossEvent.removeEventListener('leftPointDown', this._onPointerDownBind);
        this._props.dispose();
        for(let config of this._config){
            config.dispose();
        }
        for(let br of this._breaker){
            br.dispose();
        }
        this._breaker=null;
        this._props=null;
        this._config=null;
        this._objects=null;
        this._orders=null;
        this._pointerVector=null;
        this._dragStartEvent =null;
        this._dragInEvent =null;
        this._dragEndEvent =null;
        this._intCount=null;
        this._oriPointer=null;
        this._isFront=null;
        this._pointerIntersect=null;
        this._movePointer=null;
    }

    get enabled(){
        return this._props.enabled.value;
    }
    set enabled(bol){
        this._props.enabled.value=bol;
    }
    get closestRange(){
        return this._props.closestRange.value;
    }
    set closestRange(number){
        this._props.closestRange.value=number;
    }
    get onlyClosest(){
        return this._props.onlyClosest.value;
    }
    set onlyClosest(bol){
        this._props.onlyClosest.value=bol;
    }
    get shiftFrontBack(){
        return this._props.shiftFrontBack.value;
    }
    set shiftFrontBack(bol){
        this._props.shiftFrontBack.value=bol;
    }
    get stopWhenPointerOut(){
        return this._props.stopWhenPointerOut.value;
    }
    set stopWhenPointerOut(bol){
        this._props.stopWhenPointerOut.value=bol;
    }

    //----------------------------------------------私有属性及方法------------------------------------------------

    _getConfig(object){
        for(let c of this._config){
            if(c.checkType(object)){
                return c;
            }
        }
        return null;
    }
    _getObjectIndex(object,config){
        for(let i=0;i<this._objects.length;i++){
            let ob=this._objects[i];
            if(config.checkType(ob)){
                if(config.compare(object,ob)){
                    return i;
                }
            }
        }
        return -1;
    }

    _init(){
        this._initProps();
        this._initVariable();
        this._initEventBind();
        this._initPropCallback();
    }
    _initProps(){
        this._props=new Props({
            enabled:this._values.enabled!==undefined?this._values.enabled:true,//是否可用
            closestRange:this._values.closestRange||0.01,//最近筛选的范围
            onlyClosest:this._values.onlyClosest!==undefined?this._values.onlyClosest:false,//仅筛选距离最近的目标(按渲染顺序排序)
            shiftFrontBack:this._values.shiftFrontBack!==undefined?this._values.shiftFrontBack:true,//切换前后（拖动）
            stopWhenPointerOut:this._values.stopWhenPointerOut!==undefined?this._values.stopWhenPointerOut:true,//pointOut事件时停止操作
        });
    }
    _initPropCallback(){}
    _initEventBind(){
        this._fireStartEvent=(obj)=>{
            this._dragStartEvent.paras.target=obj;
            this._dragStartEvent.paras.i=this._pointerIntersect;
            this._eventCenter.dispatchEvent(this._dragStartEvent);
        };
        this._fireDragEvent=(obj,t)=>{
            this._dragInEvent.paras.target=obj;
            this._dragInEvent.paras.t=t;
            this._dragInEvent.paras.p=this._movePointer;
            this._eventCenter.dispatchEvent(this._dragInEvent);
        };
        this._fireEndEvent=(obj)=>{
            this._dragEndEvent.paras.target=obj;
            this._eventCenter.dispatchEvent(this._dragEndEvent);
        };
        this._onPointerDownBind = (e) => {
            this._onPointerDown(e);
        };
        this._onPointerMoveBind = (e) => {
            this._onPointerMove(e);
        };
        this._onPointerUpBind = (e) => {
            this._onPointerUp(e);
        };
        this._addEventListener();
    }
    _addEventListener() {
        this.crossEvent.addEventListener('leftPointDown', this._onPointerDownBind);
    }
    _initVariable(){
        this._config=[];
        this._objects=[];
        this._orders=[];
        this._breaker=[];
        this._pointerVector=new THREE.Vector2();
        this._dragStartEvent = {type: 'dragStart', paras: {p:new THREE.Vector2()}};
        this._dragInEvent = {type: 'dragIn', paras:{}};
        this._dragEndEvent = {type: 'dragEnd', paras: {}};
        this._intCount=0;
        this._oriPointer=new THREE.Vector2();
        this._isFront=true;
        this._pointerIntersect=null;
        this._movePointer=new THREE.Vector2();
    }
    _cannotStart(){
        return (
            this._dragging === true ||
            this._objects.length===0||
            this._props.enabled.value === false
        );
    }
    _pointerDownAccept(){
        if(this._props.enabled.value===false)return;
        for(let b of this._breaker){
            b.interrupt=true;
        }
        this._fireStartEvent(this._rayObject);
        this.crossEvent.addEventListener('leftPointMove', this._onPointerMoveBind);
        this.crossEvent.addEventListener('leftPointUp', this._onPointerUpBind);
        if(this._props.stopWhenPointerOut.value)this.crossEvent.addEventListener('pointOut', this._onPointerUpBind);
    }
    _onPointerDown(e) {
        if (this._cannotStart()) return;
        let pointer = e.isMouse ? new THREE.Vector2(e.clientX, e.clientY) : new THREE.Vector2(e.touches[0].clientX, e.touches[0].clientY);
        this._oriPointer.copy(pointer);
        if (pointer !== undefined) {
            this._dragStartEvent.paras.p.copy(pointer);
            this._pointerIntersect=this._intersectObjects(pointer);
            if(this._pointerIntersect){
                this._dragging = true;
                this._rayObject = this._pointerIntersect.object;
                let config=this._getConfig(this._rayObject);
                config.setDragStart(this._pointerIntersect,this._rayObject,this.camera,this._rect);
                this._pointerDownAccept();
            }
        }
    }
    _onPointerMove(e) {
        if (this._dragging === false)return;
        this._movePointer = e.isMouse ? new THREE.Vector2(e.clientX,e.clientY) :new THREE.Vector2(e.touches[0].clientX,e.touches[0].clientY);
        if(Utils.equal(this._oriPointer,this._movePointer,'vector2'))return;
        let config=this._getConfig(this._rayObject);
        config.setDragMove(this._rayObject,this._movePointer,this._rect,this.camera,this._fireDragEvent);
    }
    _onPointerUp(e) {
        if (this._dragging === false)return;
        this._dragging = false;
        this._pointerVector = new THREE.Vector2();
        this._fireEndEvent(this._rayObject);
        this._rayObject = undefined;
        this._intersectObject = undefined;
        for(let b of this._breaker){
            b.interrupt=false;
        }
        this.crossEvent.removeEventListener('leftPointMove', this._onPointerMoveBind);
        this.crossEvent.removeEventListener('leftPointUp', this._onPointerUpBind);
        if(this._props.stopWhenPointerOut.value)this.crossEvent.removeEventListener('pointOut', this._onPointerUpBind);
    }
    _intersectObjects(pointer) {
        let intersections=[];
        for(let object of this._objects){
            let config=this._getConfig(object);
            if(!config)continue;
            let intersection=config.intersectObject(object,pointer,this.camera,this._rect);
            if(Array.isArray(intersection)){
                intersections.push(...intersection);
            }else{
                intersections.push(intersection);
            }
        }
        if(intersections.length===0)return false;
        if(this._props.onlyClosest.value) {
            let closests = [];
            let renderOrders = [];
            let ref = (()=>{
                let len=Number.POSITIVE_INFINITY;
                for(let its of intersections){
                    if(its.distance<len)len=its.distance;
                }
                return len;
            })();
            for (let i = 0; i < intersections.length; i++) {
                if (Utils.zoneEqual(ref,this._props.closestRange.value,intersections[i].distance)) {
                    closests.push(intersections[i]);
                    renderOrders.push(intersections[i].object.renderOrder);
                }
            }
            let sorts = Utils.sortIndexArray(renderOrders,(a,b)=>{return a>b});
            intersections = [];
            for (let idx of sorts) {
                intersections.push(closests[idx]);
            }
        }
        let target = false;
        if (this._props.shiftFrontBack.value) {
            if (this._isFront) {
                target = intersections[0] ? intersections[0] : false;
            } else {
                if (intersections.length > 1) {
                    this._intCount++;
                    this._intCount = this._intCount > intersections.length - 1 ? 0 : this._intCount;
                    target = intersections[this._intCount] ? intersections[this._intCount] : false;
                } else {
                    target = intersections[0] ? intersections[0] : false;
                }
            }
            this._isFront = !this._isFront;
        } else {
            target = (() => {
                let indices = (() => {
                    let tp = [];
                    for (let i = 0; i < this._orders.length; i++) {
                        tp.push(null);
                    }
                    return tp;
                })();
                for (let i = 0; i < intersections.length; i++) {
                    let temp = intersections[i].object;
                    for (let j = 0; j < indices.length; j++) {
                        if (indices[j] === null && (temp instanceof this._orders[j])) {
                            indices[j] = i;
                            if (j === 0)return intersections[i];
                        }
                    }
                }
                for (let i = 0; i < indices.length; i++) {
                    if (indices[i] !== null)return intersections[indices[i]];
                }
                return intersections[0];
            })();
        }
        return target;
    }
}